/* 算法实现模板 */
import KDTree from "./KDTree";
import { KDNode } from "../../common/KDTreeFuncs";
import Vector, { toVector } from "../../common/Vector";

import DefaultValue from "./input/KDTreeFindNearest.input.json";
import { showPointsInFigure } from "../../visual/Figure2D";
import { POINT_TYPE } from "../../visual/Canvas2DItem";
import { showMathStringInTable } from "../../visual/Table2D";

class KDTreeFindNearest extends KDTree{
  constructor(){
    super();
    this.id = 'KDTreeFindNearest';
    this.DefaultValue = DefaultValue;
  }

  loadModel(json){

    if(json.TrainDS){
      let tmp = JSON.parse(json.TrainDS);
      this.trainDS.T = toVector(tmp.T, true);
    }

    if(json.Model){
      let tmp = JSON.parse(json.Model);
      let node = new KDNode();
      node.copyFromJSON(tmp.node, true);
      this.model.node = node;
    }

    if(json.TestDS){
      this.testDS.T = toVector(json.TestDS.T, true);
    }
  }

  load(jsonString){
    let json = JSON.parse(jsonString);
    if(json.TrainDS){
      let tmp = JSON.parse(json.TrainDS);
      this.trainDS.T = toVector(tmp.T, true);
    }
    if(json.Model){
      let tmp = JSON.parse(json.Model);
      let node = new KDNode();
      node.copyFromJSON(tmp.node);
      this.model.node = node;
    }
    
    if(this.figure) this.figure.clearAll();
    this.print();
  }

  displayTest(){
    const T = this.testDS.T;
    const R = this.testDS.R;
    let nearestPoints = [];
    let testTable = [];
    testTable.push([String.raw`\textbf{目标点}`, String.raw`\textbf{最近点}`, String.raw`\textbf{最近距离}`]);

    for(let i = 0, il = T.length; i < il; i++){
      testTable.push([]);
      testTable[i + 1][0] = T[i].toKatexString();
      if(R){
        testTable[i + 1][1] = R[i].nearest_point.toKatexString();
        // testTable[i + 1][2] = parseFloat(R[i].nearest_dist).fixed(2);
        testTable[i + 1][2] =String.raw`${R[i].nearest_dist.toFixed(2)}`;
        nearestPoints.push(R[i].nearest_point);
        this.figure.addCircle(T[i], R[i].nearest_point);
      }
    }

    if(this.domTest) this.domTest.innerHTML = '';
    showMathStringInTable(this.domTest, testTable);

    showPointsInFigure(this.figure, T, 5, POINT_TYPE.X_Cross, '#ff0000');
    showPointsInFigure(this.figure, nearestPoints, 8, POINT_TYPE.Circle, '#00ff00');
  }

  travel(kd_node, target, max_dist){

    const k = this.k;
    if (!kd_node){
      return {nearest_point:new Vector(k),nearest_dist:Number.MAX_VALUE, nodes_visited:0} ;
    }

    var nodes_visited = 1;
    const s = kd_node.split;
    const pivot = kd_node.dom_elt;
    let nearer_node;
    let further_node;

    if (target[s] <= pivot[s]){
      nearer_node = kd_node.left;
      further_node = kd_node.right;
    }
    else{
      nearer_node = kd_node.right;
      further_node = kd_node.left;
    }

    const temp1 = this.travel(nearer_node, target, max_dist);
    
    let nearest = temp1.nearest_point;
    let dist = temp1.nearest_dist;
    nodes_visited += temp1.nodes_visited;

    if (dist < max_dist){
      max_dist = dist;
    }
    // 判断是否相交
    let temp_dist = Math.abs(pivot[s] - target[s]);
    if( max_dist < temp_dist){
      if( dist < this.minDist ){
        this.minDist = dist;
      }
      this.travelNodes.push([kd_node.dom_elt , this.minDist]);
      return {nearest_point:nearest,nearest_dist:dist, nodes_visited:nodes_visited};
    }
    // 计算距离
    let sum = 0;
    for( let d = 0; d < k; d ++ ){
      sum += (pivot[d]-target[d])*(pivot[d]-target[d]);
    }
    temp_dist = Math.sqrt(sum);

    if (temp_dist < dist){
      nearest = pivot;
      dist = temp_dist;
      max_dist = dist; 
    }

    const temp2 = this.travel(further_node, target, max_dist);
    
    nodes_visited += temp2.nodes_visited;
    if (temp2.nearest_dist < dist){
      nearest = temp2.nearest_point;
      dist = temp2.nearest_dist;
    }

    if( dist < this.minDist ){
      this.minDist = dist;
    }

    this.travelNodes.push([kd_node.dom_elt , this.minDist]);
    return {nearest_point:nearest,nearest_dist:dist, nodes_visited:nodes_visited};
  }

  find_nearest(tree, point){
    this.travelNodes  = [];
    this.minDist      = Number.MAX_VALUE;
    this.k            = point.length;
    return this.travel(tree, point, this.minDist);
  }

  test(){
    const T = this.testDS.T;
    const tree = this.model.node;
    let result = [];
    let testLog = [];
    testLog.push([
      String.raw`\textbf{目标点}`, 
      String.raw`\textbf{搜索点}`, 
      String.raw`\textbf{步骤}`, 
      String.raw`\textbf{当前最近距离}`]);
    T.forEach(element => {
      let nearest = this.find_nearest(tree, element);
      for(let i = 0, il=this.travelNodes.length; i < il; i++){
        // console.log(this.travelNodes[i]);
        const node = new Vector(this.travelNodes[i][0], true);
        const dist = this.travelNodes[i][1].toFixed(5);
        testLog.push([
          element.toKatexString(), 
          node.toKatexString(), 
          `${i+1}`, 
          `${dist}`]);
      }
      nearest.nearest_point = new Vector(nearest.nearest_point, true);
      result.push(nearest);
      testLog.push(['----','----','----','----']);
    });

    if(this.domTable) this.domTable.innerHTML = '';
    showMathStringInTable(this.domTable, testLog);
    this.testDS.R = result;

    if(this.figure) this.figure.clearAll();
    this.print();
  }
}


export default KDTreeFindNearest;